// SPDX-FileCopyrightText: Adam Evyčędo
//
// SPDX-License-Identifier: GPL-3.0-or-later

package xyz.apiote.bimba.czwek.repo

import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Parcelable
import android.text.Annotation
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.SpannedString
import androidx.preference.PreferenceManager
import kotlinx.parcelize.Parcelize
import xyz.apiote.bimba.czwek.R
import xyz.apiote.bimba.czwek.api.StopV1
import xyz.apiote.bimba.czwek.api.StopV2
import xyz.apiote.bimba.czwek.api.StopV3
import xyz.apiote.bimba.czwek.api.transitous.model.Match
import xyz.apiote.bimba.czwek.api.transitous.model.Place
import xyz.apiote.bimba.czwek.data.ResultFlowItem


@Parcelize
data class Stop(
	val code: String,
	val stopName: String,
	val nodeName: String,
	val zone: String,
	val feedID: String?,
	val stopPosition: Position,
	val changeOptions: List<ChangeOption>,
	val description: String?
) : Queryable, Locatable, StopAbstract, Parcelable, xyz.apiote.bimba.czwek.data.traffic.Place, ResultFlowItem {
	companion object {
		fun distanceComparator(centre: Position) = object : Comparator<Stop> {
			override fun compare(
				o1: Stop?,
				o2: Stop?
			): Int {
				return Position.comparator(centre).compare(o1?.location(), o2?.location())
			}

		}
	}

	override fun icon(context: Context, scale: Float): Drawable {
		return super.icon(context, nodeName, scale)
	}

	override fun id(): String = code

	override fun location(): Position = stopPosition

	constructor(s: StopV1) : this(
		s.code,
		s.name,
		s.name,
		s.zone,
		null,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV2) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: StopV3) : this(
		s.code,
		s.name,
		s.nodeName,
		s.zone,
		s.feedID,
		Position(s.position),
		s.changeOptions.map { ChangeOption(it) },
		null
	)

	constructor(s: Match) : this(
		s.id,
		s.name,
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString() + s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		s.areas.sortedBy { it.adminLevel }.map { it.name }.distinct().joinToString()
	)

	constructor(s: Place) : this(
		s.stopId ?: "",
		s.name,
		s.name,
		"",
		"transitous",
		Position(s.lat.toDouble(), s.lon.toDouble()),
		emptyList(),
		""
	)

	fun changeOptions(context: Context, decoration: LineDecoration): Pair<Spannable, String> {
		return Pair(changeOptions.groupBy { it.line }
			.map {
				Pair(
					it.key,
					it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
				)
			}.fold(SpannableStringBuilder("")) { acc, p ->
				if (acc.toString() != "") {
					acc.append("; ")
				}
				var str = SpannableStringBuilder(
					context.getText(
						R.string.vehicle_headsign
					) as SpannedString
				)
				str = applyAnnotations(str, decoration, p.first, p.first.name, p.second)
				str = applyAnnotations(str, decoration, p.first)
				acc.append(str)
				acc
			},
			changeOptions.groupBy { it.line }
				.map {
					Pair(
						it.key,
						it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
					)
				}.joinToString {
					context.getString(
						R.string.vehicle_headsign_content_description, it.first, it.second
					)
				})
	}

	private fun applyAnnotations(
		s: SpannableStringBuilder,
		decoration: LineDecoration,
		line: LineStub,
		vararg args: Any
	): SpannableStringBuilder {
		val str = SpannableStringBuilder(s)
		val annotations = str.getSpans(0, str.length, Annotation::class.java)
		annotations.forEach {
			when (it.key) {
				"arg" -> {
					if (args.isEmpty()) {
						return@forEach
					}
					val argIndex = Integer.parseInt(it.value)
					str.replace(str.getSpanStart(it), str.getSpanEnd(it), args[argIndex] as String)
				}

				"decoration" -> {
					if (args.isNotEmpty()) {
						return@forEach
					}
					line.decorate(str, decoration, it)
				}
			}
		}
		return str
	}

	fun changeOptionsString(): String = changeOptions.groupBy { it.line }
		.map {
			Pair(
				it.key,
				it.value.flatMap { co -> co.headsigns }.sortedBy { headsign -> headsign }.joinToString()
			)
		}.joinToString("; ")

	override fun toString(): String {
		return "$stopName ($code) [$zone] $stopPosition\n${changeOptionsString()}"
	}

	override fun getName() = stopName

	override fun getDescription(context: Context, decoration: LineDecoration) = changeOptions(context, decoration).first.toString()

	override fun getLatitude() = location().positionLatitude

	override fun getLongitude() = location().positionLongitude

	override fun getJourneyID() = code

	override fun getShortName() = stopName

	override fun getPosition() = stopPosition

	enum class LineDecoration {
		NONE, ITALICS, COLOUR;

		companion object {
			fun fromPreferences(context: Context) =
				when (PreferenceManager.getDefaultSharedPreferences(context)
					.getString("line_decoration", "italics")) {
					"italics" -> ITALICS
					"colour" -> COLOUR
					"none" -> NONE
					else -> ITALICS
				}
		}
	}
}

